// 拦截器，拦截所有请求，如果发现是ssr渲染，则将数据存入map，待浏览器上渲染时，可以直接使用。
import type { AxiosInstance, AxiosRequestConfig, Method } from 'axios'
import md5 from 'md5'

const SSRDATAKEY = '__SSRDATA__' 


type MapData = {
  [k: string]: {
    data: any
    count: number
  }
}

let map = {} as MapData

const getKey = (config: AxiosRequestConfig<any>) => {
  return md5(JSON.stringify(config)).substring(0, 8)
}
export const createProxy = (http: AxiosInstance) => {
  const ssr = typeof window === 'undefined'

  const request = async (config: AxiosRequestConfig<any>) => {
    const key = getKey(config)

    if (ssr) {
      if (map[key]) {
        map[key].count++

        return map[key].data
      }
      try {
        const res = await http(config)

        let data = res

        if (res.request) data = res.data
   
        map[key] = { data, count: 1 }

        return res
      } catch (err) {
        return Promise.reject(err)
      }
    }

    if (window[SSRDATAKEY] !== undefined) {
      const map = window[SSRDATAKEY]

      if (map[key]) {
        map[key].count--
        const data = map[key].data
        if (map[key].count === 0) {
          delete map[key]
        }

        return data
      }
    }

    return http(config)
  }

  const fn = (method: Method) => {
    if (method === 'put' || method === 'post') {
      return (url: string, data: Record<string, any>, config: AxiosRequestConfig<any>) => {
        return request({ url, method, data, ...config })
      }
    }
    return (url: string, config: AxiosRequestConfig<any>) => {
      return request({ url, method, ...config })
    }
  }

  const methods = ['get', 'post', 'options', 'delete', 'put', 'patch'] as const
  const set = new Set(methods)

  return new Proxy(
    http,

    {
      get(target, key: keyof AxiosInstance) {
        if (set.has(key.toLowerCase() as typeof methods[number])) {
          return fn(key as typeof methods[number])
        }
        return http[key]
      },

      apply(target, thisBinding, args) {
        return request(args[0])
      },
    }
  ) as AxiosInstance
}

export const getCacheScript = () => {
  return `<script>window.${SSRDATAKEY}=${JSON.stringify(map)}</script>`
}

export const collect = () => {
  map = {}
  return { getScript: getCacheScript, data: map }
}
